#region Copyright (c) 2007-2008, Chris Klochek

/********************************************************************************************************************
'
' Copyright (c) 2007-2008, Chris Klochek
' All rights reserved.
' 
' Redistribution and use in source and binary forms, with or without modification, are permitted provided
' that the following conditions are met:
' 
' * Redistributions of source code must retain the above copyright notice, this list of conditions and the
' 	following disclaimer.
' 
' * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
' 	the following disclaimer in the documentation and/or other materials provided with the distribution.
' 
' * Neither the name of the author nor the names of its contributors may be used to endorse or 
' 	promote products derived from this software without specific prior written permission.
' 
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
' WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
' PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
' ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
' LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
' INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
' OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
' IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'
'*******************************************************************************************************************/

#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace SuedeTest.Core.Util {
   public class GlobalHandlers {
      private static List<IntPtr> _createdWindows = new List<IntPtr>();
      private static List<IntPtr> _hookHandles = new List<IntPtr>();
      private static HookHandler _handler;


      private const int CbtHookType = 5;
      private const int HCBT_ACTIVATE = 5;
      private const int HCBT_CREATE = 3;

      private static Win32.CBTCallback _callback = null;

      public delegate void HookHandler ();

      /// <summary>
      /// Register the handler for window activation/creation for the current thread.
      /// </summary>
      /// <param name="handler"></param>
      public static void RegisterGlobalModalHookHandler(HookHandler handler) {
         _handler = handler;
         ListenOnThread(Win32.GetCurrentThreadId ());
      }
      
      /// <summary>
      /// Register the handler for the window
      /// </summary>
      /// <param name="handler"></param>
      /// <param name="threadID"></param>
      public static void RegisterGlobalModalHookHandler(HookHandler handler, int threadID) {
         _handler = handler;
         ListenOnThread(threadID);
      }

      public static void RegisterGlobalModalHookHandlerThread(int threadID) {
         ListenOnThread(threadID);
      }

      private static void ListenOnThread(int messagePumpThreadID) {
         if (_callback == null) {
            _callback = Callback_ModalListener;
         }
         _hookHandles.Add (Win32.SetWindowsHookEx(CbtHookType, _callback, IntPtr.Zero, messagePumpThreadID));
      }

      public static void DetachAllHandlers() {
         foreach (IntPtr handle in _hookHandles) {
            if (handle != IntPtr.Zero) {
               Win32.UnhookWindowsHookEx(handle);
            }
         }
      }

      /// <summary>
      /// CBT callback called when a form is activated.
      /// If the newly activated form is modal and matches any registered names,
      /// invoke the appropriate hander.
      /// </summary>
      private static IntPtr Callback_ModalListener(int code, IntPtr wParam, IntPtr lParam) {
         if (code == HCBT_CREATE) {
            _createdWindows.Add(wParam);
         } else if (code == HCBT_ACTIVATE) {
            if (_createdWindows.Contains(wParam)) {
               _createdWindows.Remove(wParam);
               FindWindowNameAndInvokeHandler(wParam);
            }
         }

         return Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
      }

      private static void FindWindowNameAndInvokeHandler(IntPtr hwnd) {
         string name = null;

         Form form = Form.FromHandle(hwnd) as Form;
         if (form != null && form.Modal) {
            name = form.Name;
         } else if (WindowHandle.IsDialog(hwnd)) {
            name = WindowHandle.GetCaption(hwnd);
            if (name == null) {
               name = string.Empty;
            }
         }

         Invoke(name, hwnd);
      }

      private static void Invoke(string name, IntPtr hWnd) {
         if (name == null) {
            return;
         }

         if (_handler != null) {
            _handler();
         }
      }
   }
}
